import optuna
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn import preprocessing
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import classification_report
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import TomekLinks
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier

from tools.plot_model import plot_singleModel

import warnings

warnings.filterwarnings("ignore")


def best_dt_params(filePath):
    data = pd.read_csv(filePath)
    # print(data)
    column_names = ['loc', 'v(g)', 'ev(g)', 'iv(g)', 'n', 'v', 'l', 'd', 'i', 'e', 'b', 't', 'lOCode', 'lOComment',
                    'lOBlank', 'lOCodeAndComment', 'uniq_Op', 'uniq_Opnd', 'total_Op', 'total_Opnd', 'branchCount']
    # print(column_names)
    df = pd.DataFrame(data=data, columns=column_names)
    # print(df)
    y = pd.DataFrame(data=data, columns=['defects'])
    # print(y)

    # 创建 SMOTE 对象
    smote = SMOTE(sampling_strategy=1.0, random_state=42)  # 1.0 表示将类别1的数量提升到类别0的数量
    # 使用 SMOTE 进行过采样
    X_resampled, y_resampled = smote.fit_resample(df, y)

    # 使用 TomekLinks 进行欠采样
    tl = TomekLinks(sampling_strategy='auto')
    X_resampled, y_resampled = tl.fit_resample(X_resampled, y_resampled)

    # 创建PCA实例并拟合数据
    pca = PCA(n_components=3)  # 选择要保留的主成分数量
    X_pca = pca.fit_transform(X_resampled)  # X是你的特征矩阵

    def objective(trial):
        # 定义参数搜索范围

        max_depth = trial.suggest_int("max_depth", 10, 100)
        max_leaf_nodes = trial.suggest_int("max_leaf_nodes", 5, 20)
        min_samples_split = trial.suggest_int("min_samples_split", 2, 20)
        min_samples_leaf = trial.suggest_int("min_samples_leaf", 1, 20)
        # 参数说明
        # criterion：特征选择标准，【entropy, gini】。默认gini，即CART算法。
        # max_depth：决策树最大深度，常用的可以取值10-100之间，常用来解决过拟合。
        # min_samples_split：内部节点（即判断条件）再划分所需最小样本数
        # min_samples_leaf：叶子节点（即分类）最少样本数。这个值限制了叶子节点最少的样本数，如果某叶子节点数目小于样本数，则会和兄弟节点一起被剪枝。
        # max_leaf_nodes：最大叶子节点数。【int, None】，通过设置最大叶子节点数，可以防止过拟合。
        # 默认值None，默认情况下不设置最大叶子节点数。如果加了限制，算法会建立在最大叶子节点数内最优的决策树。
        # 如果特征不多，可以不考虑这个值，但是如果特征多，可以加限制，具体的值可以通过交叉验证得到。

        # 创建决策树分类器
        clf = DecisionTreeClassifier(criterion='entropy', max_depth=max_depth, max_leaf_nodes=max_leaf_nodes,
                                     min_samples_leaf=min_samples_leaf, min_samples_split=min_samples_split)

        X_train, X_test, y_train, y_test = train_test_split(X_pca, y_resampled, test_size=0.3)

        # 训练模型
        clf.fit(X_train, y_train)

        # 使用模型进行预测
        y_pred = clf.predict(X_test)

        # 计算准确性作为优化目标
        recall = recall_score(y_test, y_pred)

        return recall

    # 创建Optuna的study对象
    study = optuna.create_study(direction="maximize")  # 最大化准确性
    study.optimize(objective, n_trials=1000)  # 运行100次试验

    # 获取最佳参数组合
    best_params = study.best_params
    best_score = study.best_value

    print("最佳参数:", best_params)
    print("最佳准确性:", best_score)


if __name__ == "__main__":
    filePath = "../../data/KC2.csv"
    best_dt_params(filePath)
